/**
 * Copyright @ 2023 iAuto (Shanghai) Co., Ltd.
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are NOT permitted except as agreed by
 * iAuto (Shanghai) Co., Ltd.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */
import { genUid } from '@/lib/blocks-vm';
import { ElementDype } from '@/lib/dataType';
import log from '@/lib/log';
import { isArray, isObject, isString } from 'lodash';
import md5 from 'js-md5';
import ArgumentTypes from '@scene/scene-vm/src/extension-support/argument-type';
import { getType } from '@/lib/custom-extension';
import { getSkillId } from '.';
import { TaskUploadStatus } from '@/models/soaTask';

type Block = Record<string, any>;
export type SceneData = Record<string, Block>;
type FieldName = 'TEXT' | 'NUM';

// 检验场景数据里面 是否存在标记数据
export function checkPlaceholdersAndParams(sceneData: SceneData, markIds: number[]): boolean {
  if (!isArray(markIds)) {
    log.error('markIds should be an array');
    return false;
  }
  // 校验场景数据里面有没有标记数据
  return Object.entries(sceneData).some((item: [string, any]) => {
    const opcode = item[1].opcode as string;
    const skillId = getSkillId(opcode);
    return skillId ? markIds.includes(Number(skillId)) : false;
  });
}

// 创建一个普通的参数积木
function createArgBlock(
  arg: { opcode: string; parent: string; val: string },
  fieldName: FieldName = 'TEXT',
) {
  return {
    next: null,
    inputs: {},
    shadow: true,
    topLevel: false,
    opcode: arg.opcode,
    parent: arg.parent,
    fields: {
      [fieldName]: [arg.val || '', null],
    },
  };
}
function getFieldName(desc: string): FieldName {
  if (!desc || !isString(desc)) {
    return 'TEXT';
  }

  const dataType = desc.split(',')[0];
  return getType(dataType) === ArgumentTypes.STRING ? 'TEXT' : 'NUM';
}

// 获取参数积木
function getFieldBlock(blockDb: Record<string, Block>, block: Block, fieldName: string): Block {
  // 读取参数 key
  const fieldKey = block['inputs'][fieldName][1];
  return blockDb[fieldKey];
}

// 获取积木参数值
function getInputValue(blockDb: Record<string, Block>, block: Block, fieldName: string): string {
  const key = block['inputs']?.[fieldName]?.[1] || '';
  if (!key) {
    return '';
  }
  const fieldBlock = getFieldBlock(blockDb, block, fieldName);
  return fieldBlock?.['fields']?.['TEXT']?.[0] ?? '';
}

// 设置值
function setFieldValue(block: Block, fieldName: FieldName = 'TEXT', fieldVale: string) {
  block['fields'][fieldName][0] = fieldVale;
}

// 设置积木入参
function setInputKey(block: Block, inpKey: string, blockKey: string) {
  Object.assign(block?.['inputs'], {
    [inpKey]: [1, blockKey],
  });
}

export function replaceBackward(
  sceneData: SceneData,
  skillJson: any[],
  ids: number[],
  fileMap: Map<string, string>,
): any {
  if (ids.length === 0) {
    console.warn('ids 不应该为空');
    return sceneData;
  }

  for (const key in sceneData) {
    const block = sceneData[key];
    const skillId = getSkillId(block.opcode);

    if (shouldProcessBlock(Number(skillId), ids, block)) {
      processBlock(sceneData, block, skillJson, fileMap, key);
    }
  }

  return sceneData;
}

// 校验积木是否符合需要
function shouldProcessBlock(skillId: number, ids: number[], block: Block): boolean {
  return ids.includes(Number(skillId)) && block.opcode?.split('_')?.length === 2;
}

function processBlock(
  sceneData: SceneData,
  block: Block,
  skillJson: any[],
  fileMap: Map<string, string>,
  parentKey: string,
): void {
  const skill = getSkill(skillJson, Number(getSkillId(block.opcode)));

  if (skill) {
    if (isMediaSkill(skill)) {
      processMediaSkill(sceneData, block, skill, fileMap, parentKey);
    } else {
      processRegularSkill(sceneData, block, skill, parentKey);
    }
  }
}

// 判断是不是media积木
function isMediaSkill(skill: any): boolean {
  return skill?.mInputArgs?.some((arg: any) => {
    if (arg?._mDesc?.split(',')[1] === ElementDype.MEDIA) {
      return true;
    }
    return false;
  });
}

function processMediaSkill(
  sceneData: SceneData,
  block: Block,
  skill: any,
  fileMap: Map<string, string>,
  parentKey: string,
): void {
  // 参数键
  const lastInputKey = createKey([skill?.mInputArgs[1].mName, skill.mID]);
  const fieldName = getFieldName(skill?.mInputArgs[1]._mDesc);

  //! 判断有没有第二个值 没有的话创建一个参数积木
  if (outputBlockParamNumber(block) === 1) {
    // 创建opcode
    const opcode = createKey([md5(skill.category[0]), 'text', lastInputKey]);

    // 需要创建一个参数积木
    const argBlock = createArgBlock(
      {
        parent: parentKey,
        opcode: opcode,
        val: fileMap.get(parentKey) || '',
      },
      fieldName,
    );

    // 创建唯一id
    const argBlockKey = createUid(sceneData);
    // 设置参数
    setInputKey(block, lastInputKey, argBlockKey);
    // 插入参数积木
    sceneData[argBlockKey] = argBlock;
  } else {
    // 存在文件id积木
    // 获取参数value
    const lastVal = getInputValue(sceneData, block, lastInputKey);
    // 获取新的文件id
    const fileUuid = fileMap.get(parentKey) as string;
    // 判断 media 积木 id 有没有发生变化
    if (fileUuid && lastVal !== fileUuid) {
      // 不相等的话 设置新值
      const fieldBlock = getFieldBlock(sceneData, block, lastInputKey);
      setFieldValue(fieldBlock, fieldName, fileUuid);
    }
  }
}

function processRegularSkill(
  sceneData: SceneData,
  block: Block,
  skill: any,
  parentKey: string,
): void {
  // 处理普通积木逻辑
  // 普通积木
  // 读取积木参数数量
  const len = outputBlockParamNumber(block);
  // 实际参数的数量
  const arglen = skill?.mInputArgs.length;
  if (len !== arglen) {
    // 不相等的话 创建剩余的积木 设置初始值
    (skill?.mInputArgs as any[])?.map((arg, index) => {
      // 判断
      if (index > len - 1) {
        // 创建参数名
        const paramName = createKey([arg.mName, skill.mID]);
        // 创建opcode
        const opcode = createKey([md5(skill.category[0]), 'text', paramName]);
        const fieldName = getFieldName(arg._mDesc);
        // 需要创建一个参数积木
        const argBlock = createArgBlock(
          {
            parent: parentKey,
            opcode: opcode,
            val: arg.mDefaultValue || '',
          },
          fieldName,
        );

        // 创建唯一id
        const argBlockKey = createUid(sceneData);
        // 设置参数
        setInputKey(block, paramName, argBlockKey);
        // 插入参数积木
        sceneData[argBlockKey] = argBlock;
      }
    });
  }
}

// 其他辅助函数
// ...

// 创建一个唯一的uid
function createUid(blockDb: Record<string, Block>): string {
  let uid = genUid() as string;
  while (blockDb[uid]) {
    uid = genUid() as string;
  }
  return uid;
}

// 创建 _ key
function createKey(args: (string | number)[]): string {
  return args.join('_');
}

// 输出参数数量
function outputBlockParamNumber(block: Block, key: 'inputs' | 'fields' = 'inputs'): number {
  if (!block) {
    log.warn('block 是必传的');
    return 0;
  }

  if (!isObject(block)) {
    log.warn('block 是object 类型的');
    return 0;
  }

  return Object.entries(block[key] as object).length;
}

// 替换场景数据
export function replaceDateCustomData(sceneData: any, skillJson: any[], ids: number[]): any {
  // 在这里实现替换场景数据的逻辑
  // 返回替换后的场景数据

  if (ids.length === 0) {
    log.warn('ids 不应该为空');
    return sceneData;
  }

  for (const key in sceneData) {
    const block = sceneData[key];
    const skillId = getSkillId(block.opcode);
    if (ids.includes(Number(skillId)) && block.opcode?.split('_')?.length === 2) {
      // 找到目标数据
      // 获取能力
      const skill = getSkill(skillJson, Number(skillId));
      if (skill) {
        // 查询 date_custom 参数
        skill.mInputArgs.map((arg: any) => {
          if (arg._mDesc.includes(ElementDype.DATE_CUSTOM)) {
            // 参数列表 Map 数据
            const selectMap = getSelectListMap(arg._mDesc);
            // 参数名
            const paramName = createKey([arg.mName, skillId]);
            // 参数积木
            const argBlock = getFieldBlock(sceneData, block, paramName);
            // 目标积木的值
            const argVal = getInputValue(sceneData, block, paramName);
            const fieldName = getFieldName(arg._mDesc);
            // 处理新值
            const newArgVal = argVal
              .split(',')
              .map((v) => selectMap.get(v))
              .join(',');

            // 设置值
            setFieldValue(argBlock, fieldName, newArgVal);
          }
        });
      }
    }
  }

  return sceneData;
}

function getSelectListMap(desc: string): Map<string, string> {
  const descMap = new Map<string, string>([]);
  const [, , value] = desc.split(',');
  const valList = value.split('*');

  if (isArray(valList)) {
    valList.forEach((val: string) => {
      if (isString(val)) {
        const [key, value] = val.split(':');
        descMap.set(key, value);
        descMap.set(value, key);
      }
    });
  }
  return descMap;
}

function getSkill(skillJson: any[], skillId: number) {
  return skillJson.find((item) => item.mID === skillId);
}

export const useModal = (dispatch: any, namespace: string) => {
  const set = (key: string, value: any) => {
    dispatch({
      type: `${namespace}/setGlobalState`,
      payload: {
        [key]: value,
      },
    });
  };

  return {
    set,
  };
};

// 读取 Local Storage 中的值
export function getLocalStorage(key: string, defaultValue: any) {
  try {
    const item = window.localStorage.getItem(key);
    return item ? JSON.parse(item) : defaultValue;
  } catch (error) {
    console.error('Error retrieving data from local storage:', error);
    return defaultValue;
  }
}

// 设置 Local Storage 中的值
export function setLocalStorage(key: string, value: any) {
  try {
    window.localStorage.setItem(key, JSON.stringify(value));
  } catch (error) {
    console.error('Error storing data to local storage:', error);
  }
}

let timer: NodeJS.Timeout | 0 = 0
export const checkUploadStatus = (): Promise<TaskUploadStatus> => {
  return new Promise((resolve, reject) => {
      // 定时查询
      timer = setInterval(() => {
          // 读取本地数据
          const uploadStatus = getLocalStorage('uploadStatus', 'pending')
          // 查询状态
          if (uploadStatus !== 'pending') {
              resolve(uploadStatus)
              clearInterval(timer)
          }
        }, 1000)
  })
}

export const initLocalStorage = () => {
  if (timer) {
    clearInterval(timer)
  }
  setLocalStorage('isConfirm', false)
  setLocalStorage('isConfirmUpdate', false)
  setLocalStorage('uploadStatus', 'pending')
}
